<!DOCTYPE html>
<html lang="zh-CN" id="html-root">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AI Studio Proxy - Chat UI</title>
    <style>
        /* --- Modernized M3-Inspired Styles --- */
        :root {
            /* Light Mode Palette (M3 Baseline Blue - More Standard) */
            /* Define RGB for RGBA usage */
            --primary-rgb: 52, 110, 248;
            --secondary-rgb: 88, 94, 113;
            --on-surface-rgb: 26, 28, 30;

            --bg-color: #f7f9fc; /* Neutral Light Background */
            --container-bg: #fefbff; /* Surface */
            --text-color: #1a1c1e; /* On Surface / On Background */
            --primary-color: rgb(var(--primary-rgb)); /* #346EF8 */
            --on-primary: #ffffff;
            --primary-container: #d8e2ff;
            --on-primary-container: #001a42;
            --secondary-color: rgb(var(--secondary-rgb)); /* #585E71 */
            --on-secondary: #ffffff;
            --secondary-container: #dde2f9;
            --on-secondary-container: #151b2c;
            /* --tertiary-color: #745470; (Example, not mapped) */
            /* --on-tertiary: #ffffff; */
            /* --tertiary-container: #ffd7f6; */
            /* --on-tertiary-container: #2b122a; */
            --user-msg-bg: var(--primary-container);
            --user-msg-text: var(--on-primary-container);
            --assistant-msg-bg: #e1e2ec; /* Surface Variant */
            --assistant-msg-text: #44474f; /* On Surface Variant */
            --system-msg-bg: #f0f4f9; /* Light neutral gray */
            --system-msg-text: #6c757d; /* Mid gray */
            --error-color: #ba1a1a;
            --on-error: #ffffff;
            --error-container: #ffdad6;
            --on-error-container: #410002;
            --error-msg-bg: var(--error-container);
            --error-msg-text: var(--on-error-container);
            --border-color: #74777f; /* Outline */
            --input-bg: var(--container-bg); /* Surface, like Material Text Field */
            --input-border: var(--border-color); /* Outline */
            --input-focus-border: var(--primary-color);
            --input-focus-shadow: 0 0 0 3px rgba(var(--primary-rgb), 0.1); /* Lighter focus */
            --button-bg: var(--primary-color);
            --button-text: var(--on-primary);
            --button-hover-bg: #1A59E0; /* Darker primary */
            --button-disabled-bg: rgba(var(--on-surface-rgb), 0.12);
            --button-disabled-text: rgba(var(--on-surface-rgb), 0.38);
            --clear-button-bg: var(--secondary-color);
            --clear-button-text: var(--on-secondary);
            --clear-button-hover-bg: #41475A; /* Darker secondary */
            --sidebar-bg: #f0f3fa; /* Slightly darker than background */
            --sidebar-border: var(--border-color);
            --icon-button-bg: transparent;
            --icon-button-hover-bg: rgba(var(--primary-rgb), 0.08);
            --icon-button-color: #44474f; /* On Surface variant */
            --log-terminal-bg: #1e1e1e; /* Keep dark */
            --log-terminal-text: #d4d4d4; /* Keep light */

            --theme-toggle-hover-bg: rgba(var(--secondary-rgb), 0.08);
            --theme-toggle-color: var(--icon-button-color);
            --theme-toggle-bg: transparent;

            --card-bg: var(--container-bg);
            --card-border: var(--border-color);
            --card-shadow: var(--shadow-sm);

            --border-radius-sm: 4px;  /* M3 Small */
            --border-radius-md: 8px;  /* M3 Medium */
            --border-radius-lg: 16px; /* M3 Large */
            --border-radius-xl: 28px; /* M3 Extra Large / Pill */
            --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.08); /* M3 Elevation 1 */
            --shadow-md: 0 2px 4px rgba(0, 0, 0, 0.1), 0 4px 6px rgba(0, 0, 0, 0.08); /* M3 Elevation 2 approx */
            --sidebar-width: 380px;
            --sidebar-transition: width 0.3s ease, padding 0.3s ease, border 0.3s ease, transform 0.3s ease;
            --content-padding: 24px;
        }

        /* Dark Mode Palette - Applied via class on <html> */
        html.dark-mode {
             /* Define RGB for RGBA usage */
            --primary-rgb: 173, 198, 255;
            --secondary-rgb: 188, 194, 217;
            --on-surface-rgb: 227, 226, 230;

            --bg-color: #1a1c1e; /* On Surface / On Background */
            --container-bg: #1a1c1e; /* Surface (often same as background in dark) */
            --text-color: #e3e2e6; /* On Surface / On Background */
            --primary-color: rgb(var(--primary-rgb)); /* #ADC6FF */
            --on-primary: #002e68;
            --primary-container: #1853b5; /* Adjusted primary container dark */
            --on-primary-container: #d8e2ff;
            --secondary-color: rgb(var(--secondary-rgb)); /* #BCC2D9 */
            --on-secondary: #2a3042;
            --secondary-container: #404659;
            --on-secondary-container: #dde2f9;
            /* --tertiary-color: #E2BBDB; */
            /* --on-tertiary: #422740; */
            /* --tertiary-container: #5B3D57; */
            /* --on-tertiary-container: #FFD7F6; */
            --user-msg-bg: var(--primary-container);
            --user-msg-text: var(--on-primary-container);
            --assistant-msg-bg: #44474f; /* On Surface Variant */
            --assistant-msg-text: #c4c6cf; /* Outline */
            --system-msg-bg: #2e3133; /* Darker neutral */
            --system-msg-text: #b9bfc2; /* Mid-light gray */
            --error-color: #ffb4ab;
            --on-error: #690005;
            --error-container: #93000a;
            --on-error-container: #ffdad6;
            --error-msg-bg: var(--error-container);
            --error-msg-text: var(--on-error-container);
            --border-color: #8e9099; /* Outline */
            --input-bg: var(--container-bg); /* Surface */
            --input-border: var(--border-color); /* Outline */
            --input-focus-border: var(--primary-color);
            --input-focus-shadow: 0 0 0 3px rgba(var(--primary-rgb), 0.15); /* Lighter focus */
            --button-bg: var(--primary-color);
            --button-text: var(--on-primary);
            --button-hover-bg: #C7D8FF; /* Lighter primary */
            --button-disabled-bg: rgba(var(--on-surface-rgb), 0.12);
            --button-disabled-text: rgba(var(--on-surface-rgb), 0.38);
            --clear-button-bg: var(--secondary-color);
            --clear-button-text: var(--on-secondary);
            --clear-button-hover-bg: #D5DAF1; /* Lighter secondary */
            --sidebar-bg: #1e2023; /* Slightly darker */
            --sidebar-border: var(--border-color);
            --icon-button-hover-bg: rgba(var(--primary-rgb), 0.08);
            --icon-button-color: #c4c6cf; /* Outline */
            --log-terminal-bg: #1e1e1e; /* Consistent dark */
            --log-terminal-text: #d4d4d4; /* Consistent light */

            --theme-toggle-hover-bg: rgba(var(--secondary-rgb), 0.08);
            --theme-toggle-color: var(--icon-button-color);
            --theme-toggle-bg: transparent;

            --card-bg: var(--container-bg);
            --card-border: var(--border-color);
            --card-shadow: 0 2px 6px rgba(0, 0, 0, 0.2), 0 4px 8px rgba(0, 0, 0, 0.15); /* M3 Elevation 2 dark approx */
        }

        *, *::before, *::after {
            box-sizing: border-box; /* Better box model */
        }

        body {
            background-color: var(--bg-color);
            color: var(--text-color);
            font-family: "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; /* Modern font stack */
            margin: 0;
            padding: 0;
            display: flex;
            height: 100vh;
            overflow: hidden;
            font-size: 16px; /* Base font size */
        }

        /* --- Workspace Layout (Chat Left, Sidebar Right) --- */
        .workspace-container {
            display: flex;
            width: 100%;
            height: 100%;
        }

        .chat-panel {
            flex-grow: 1; /* Takes available space */
            display: flex;
            flex-direction: column;
            height: 100%;
            overflow: hidden;
            background-color: var(--container-bg);
        }

        .sidebar-panel {
            width: var(--sidebar-width);
            height: 100%;
            flex-shrink: 0; /* Prevents shrinking */
            display: flex;
            flex-direction: column;
            overflow: hidden;
            background-color: var(--sidebar-bg);
            border-left: 1px solid var(--sidebar-border); /* Sidebar on the right */
            transition: var(--sidebar-transition);
            position: relative; /* Keep relative for absolute positioning of button INSIDE */
        }

        .sidebar-panel.collapsed {
            width: 0;
            padding: 0;
            border-left: none;
            overflow: hidden;
            /* transform: translateX(100%); REMOVED - Handled differently below */
            /* visibility: hidden; */ /* Using width/border is sufficient */
        }

        /* Sidebar Toggle Button (Chevron Style) */
        #toggleSidebarButton {
            position: absolute; /* Base position is absolute (for desktop) */
            top: 12px; /* Align better with header padding */
            /* left is calculated dynamically below - REMOVED */
            /* left: -14px; /* Position centered on the left edge of the parent - REMOVED */
            z-index: 10; /* Default z-index (increased for mobile below) */
            width: 28px; /* Smaller */
            height: 28px;
            border-radius: 50%;
            border: 1px solid var(--border-color);
            background-color: var(--sidebar-bg);
            color: var(--icon-button-color);
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            padding: 0;
            font-size: 1em; /* Smaller icon */
            transition: background-color 0.2s, color 0.2s, transform 0.3s ease, left 0.3s ease, border-color 0.2s;
            box-shadow: var(--shadow-sm);
        }
        #toggleSidebarButton:hover {
            /* background-color: var(--icon-button-hover-bg); REMOVED hover effect */
            border-color: var(--primary-color);
            color: var(--primary-color);
        }
        /* Specific positioning and icon logic is in JS and @media */

        h1 {
            color: var(--text-color);
            text-align: center;
            margin: 0;
            padding: 8px var(--content-padding); /* Toolbar padding */
            gap: 8px; /* Gap between buttons */
            background-color: var(--container-bg); /* Match chat panel */
            font-size: 1.2em; /* Slightly smaller title */
            font-weight: 600; /* Slightly bolder */
            flex-shrink: 0;
        }

        a {
            color: var(--primary-color);
            text-decoration: none;
            font-weight: 500;
        }
        a:hover {
            text-decoration: underline;
            filter: brightness(0.9);
        }

        strong {
            font-weight: 600;
        }

        #chatbox {
            flex-grow: 1;
            overflow-y: auto;
            padding: var(--content-padding);
            display: flex;
            flex-direction: column;
            gap: 16px; /* M3 Spacing: Adjusted from 18px */
        }

        .message {
            padding: 16px; /* M3 Padding: Adjusted from 12px 18px */
            border-radius: var(--border-radius-lg); /* Default, overridden below */
            max-width: 85%;
            word-wrap: break-word;
            line-height: 1.6;
            box-shadow: var(--shadow-sm); /* Add subtle shadow */
            border: 1px solid transparent; /* Ensure border space for consistency */
            position: relative; /* For positioning edit/delete buttons */
            transition: background-color 0.2s;
            /* Remove inconsistent large radius - handled by specific messages */
            /* border-radius: var(--border-radius-lg); */
        }
        .message:hover .message-actions {
            opacity: 1;
        }

        .user-message {
            background-color: var(--user-msg-bg);
            color: var(--user-msg-text);
            align-self: flex-end;
            margin-left: auto;
             /* M3 Speech bubble effect - Rounded corners away from pointer */
            border-radius: var(--border-radius-lg) var(--border-radius-sm) var(--border-radius-lg) var(--border-radius-lg);
            border: 1px solid rgba(var(--on-surface-rgb), 0.1);
        }
        html.dark-mode .user-message { border-color: rgba(var(--on-surface-rgb), 0.15); }

        .assistant-message {
            background-color: var(--assistant-msg-bg);
            color: var(--assistant-msg-text);
            align-self: flex-start;
            margin-right: auto;
            white-space: pre-wrap;
             /* M3 Speech bubble effect - Rounded corners away from pointer */
            border-radius: var(--border-radius-sm) var(--border-radius-lg) var(--border-radius-lg) var(--border-radius-lg);
            border: 1px solid rgba(var(--on-surface-rgb), 0.1);
        }
        html.dark-mode .assistant-message { border-color: rgba(var(--on-surface-rgb), 0.15); }


        .system-message {
            font-style: normal;
            color: var(--system-msg-text);
            font-size: 0.9em; /* Slightly larger */
            text-align: center;
            padding: 8px 12px; /* More padding */
            margin: 10px auto; /* More margin */
            max-width: 80%;
            background-color: var(--system-msg-bg);
            border-radius: var(--border-radius-md);
            border: 1px solid var(--border-color); /* Add border */
            box-shadow: none; /* No shadow for system */
        }

        .error-message {
            background-color: var(--error-msg-bg);
            border: 1px solid transparent;
            color: var(--error-msg-text);
            align-self: stretch;
            text-align: center;
            padding: 12px 18px; /* More padding */
            border-radius: var(--border-radius-md);
            margin: 10px 5%; /* Center with margin */
            box-shadow: none;
        }
        html.dark-mode .error-message { border-color: #f5c6cb; }

        #input-area {
            display: flex;
            padding: 16px var(--content-padding); /* M3 Padding: Adjusted from 15px */
            border-top: 1px solid var(--border-color);
            flex-shrink: 0;
            gap: 16px; /* M3 Spacing: Adjusted from 12px */
            align-items: flex-end;
            background-color: var(--container-bg); /* Match chat panel */
            flex-wrap: wrap; /* Allow wrapping on small screens */
        }

        #userInput {
            flex-grow: 1;
            flex-basis: 300px; /* Base width before growing */
            padding: 14px 18px; /* M3 Padding: Adjusted vertical from 12px */
            background-color: var(--input-bg);
            color: var(--text-color);
            border: 1px solid var(--input-border);
            border-radius: var(--border-radius-xl); /* Use largest radius for pill shape */
            resize: none;
            font-family: inherit;
            font-size: 1em;
            min-height: 48px; /* Ensure enough height for ~2 lines + padding */
            max-height: 200px; /* Increased max height */
            overflow-y: auto;
            line-height: 1.5;
            outline: none;
            box-shadow: var(--shadow-sm);
            transition: border-color 0.2s, box-shadow 0.2s;
        }
        #userInput:focus {
             border-color: var(--input-focus-border);
             box-shadow: var(--input-focus-shadow);
        }

        /* Buttons common style */
        .action-button {
            padding: 12px 20px; /* Increased padding */
            border: none;
            border-radius: var(--border-radius-xl); /* Use largest radius */
            cursor: pointer;
            font-family: inherit;
            font-size: 1em; /* Match input font size */
            font-weight: 500;
            transition: background-color 0.2s, transform 0.1s, box-shadow 0.2s;
            line-height: 1.5;
            height: 48px; /* Match input min-height */
            align-self: flex-end;
            box-shadow: var(--shadow-sm);
             flex-shrink: 0; /* Prevent buttons shrinking too much */
        }
        .action-button:disabled {
            cursor: not-allowed;
            box-shadow: none;
            background-color: var(--button-disabled-bg); /* Use specific disabled bg */
            color: var(--button-disabled-text); /* Use specific disabled text */
            transform: none;
        }
        .action-button:hover:not(:disabled) {
            box-shadow: var(--shadow-md);
            transform: translateY(-1px); /* Subtle lift */
        }
        .action-button:active:not(:disabled) {
            transform: translateY(0px); /* Push down */
             box-shadow: var(--shadow-sm);
        }


        #sendButton {
            background-color: var(--button-bg);
            color: var(--button-text);
        }
        #sendButton:hover:not(:disabled) {
            background-color: var(--button-hover-bg);
        }

        #clearButton {
            background-color: var(--clear-button-bg);
            color: var(--clear-button-text);
            order: 1; /* Move clear button visually before send on wrap */
        }
        #clearButton:hover:not(:disabled) {
             background-color: var(--clear-button-hover-bg);
        }

        /* Icon button style (for potential future use or log clear) */
        .icon-button {
            background-color: var(--icon-button-bg);
            color: var(--icon-button-color);
            border: none;
            border-radius: 50%;
            width: 36px;
            height: 36px;
            padding: 0; /* Reset padding */
            display: inline-flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
            font-size: 1.2em;
            transition: background-color 0.2s;
            flex-shrink: 0;
        }
        .icon-button:hover:not(:disabled) {
            background-color: var(--icon-button-hover-bg);
        }
        .icon-button:disabled {
            color: var(--button-disabled-text);
            cursor: not-allowed;
            background-color: transparent;
        }


        /* Blinking cursor */
        .assistant-message.streaming::after {
            content: '|';
            animation: blink 1s step-end infinite;
            margin-left: 2px; /* More space */
            display: inline-block;
            font-weight: bold;
            position: relative;
            top: -1px;
            opacity: 0.7; /* Make it slightly subtle */
        }
        @keyframes blink {
            from, to { opacity: 0.7; }
            50% { opacity: 0; }
        }

        /* --- Sidebar Content Styles --- */
        #api-info-area {
            padding: 15px var(--content-padding);
            border-bottom: 1px solid var(--sidebar-border);
            background-color: var(--sidebar-bg);
            font-size: 0.9em;
            color: var(--text-color);
            flex-shrink: 0;
        }
        #api-info-area details {
             transition: none;
        }
         #api-info-area summary {
             cursor: pointer;
             font-weight: 600; /* Bolder summary */
             color: var(--text-color); /* Use standard text color */
             list-style: none;
             padding-left: 1.4em; /* More space for marker */
             position: relative;
             padding-bottom: 5px; /* Space below summary */
             margin-bottom: 5px; /* Space before content */
        }
        #api-info-area summary::-webkit-details-marker {
             display: none;
        }
        #api-info-area summary::before {
             content: '▶';
             position: absolute;
             left: 0;
             font-size: 0.8em;
             transition: transform 0.2s;
             display: inline-block;
             color: var(--primary-color); /* Use primary color for marker */
             top: 50%;
             transform: translateY(-50%) rotate(0deg);
        }
        #api-info-area details[open] summary::before {
             transform: translateY(-50%) rotate(90deg);
        }
        #api-info-content {
             margin-top: 10px;
             padding-left: 1.4em; /* Indent content */
             font-size: 0.95em; /* Slightly larger content text */
        }
        #api-info-content pre {
             background-color: var(--input-bg);
             padding: 10px 15px; /* Consistent padding */
             border-radius: var(--border-radius-sm);
             border: 1px solid var(--input-border);
             overflow-x: auto;
             white-space: pre-wrap;
             word-wrap: break-word;
             font-family: "Consolas", "Monaco", monospace; /* Monospace font */
             font-size: 0.9em;
             margin: 8px 0;
             box-shadow: inset 0 1px 2px rgba(0,0,0,0.05);
        }
        html.dark-mode #api-info-content pre { box-shadow: inset 0 1px 2px rgba(255,255,255,0.05); }
         #api-info-content strong { color: var(--primary-color); font-weight: 500; }

        /* --- Log Terminal Styles (Adjusted for Sidebar) --- */
        #log-area {
            flex-grow: 1;
            display: flex;
            flex-direction: column;
            overflow: hidden;
            padding: 0;
            border-top: none;
            background-color: var(--sidebar-bg); /* Match sidebar */
        }
        #log-area-header {
            display: flex; /* Use flex for alignment */
            justify-content: space-between; /* Space out title and button */
            align-items: center; /* Vertically center */
             padding: 10px var(--content-padding);
             font-weight: 600; /* Bolder header */
             color: var(--text-color); /* Use standard text color */
             flex-shrink: 0;
             border-bottom: 1px solid var(--sidebar-border);
        }
        #clearLogButton { /* Style the new clear log button */
            margin-left: 10px; /* Space from title */
            font-size: 0.8em; /* Smaller text */
            padding: 4px 8px; /* Smaller padding */
            height: auto; /* Adjust height */
            line-height: 1.4;
            border-radius: var(--border-radius-sm); /* Smaller radius */
            background-color: var(--clear-button-bg);
            color: var(--clear-button-text);
            opacity: 0.8;
        }
        #clearLogButton:hover:not(:disabled) {
            opacity: 1;
            background-color: var(--clear-button-hover-bg);
        }

        #log-terminal-wrapper {
            flex-grow: 1;
            overflow: hidden;
            border: none;
            border-radius: 0;
            margin: 0;
            padding: 0;
            background-color: var(--log-terminal-bg); /* Set background here */
        }
        #log-terminal {
            height: 100%;
            background-color: var(--log-terminal-bg);
            color: var(--log-terminal-text);
            font-family: "Consolas", "Monaco", monospace;
            font-size: 0.85em; /* Slightly larger log font */
            padding: 10px;
            overflow-y: auto;
            white-space: pre-wrap;
            word-break: break-all;
            box-sizing: border-box;
        }
        .log-entry {
            margin-bottom: 4px; /* Slightly more space */
            line-height: 1.4;
        }
        .log-status {
            font-size: 0.85em; /* Match log font */
            margin-top: 0; /* Remove top margin */
            padding: 8px 10px; /* Consistent padding */
            color: var(--system-msg-text);
            flex-shrink: 0;
            background-color: var(--log-terminal-bg); /* Match terminal bg */
            border-top: 1px dashed var(--border-color); /* Use dashed border */
            text-align: center; /* Center status */
        }
        html.dark-mode .log-status {
            border-top-color: #454545; /* Darker border in dark mode */
            color: #a0a0a0; /* Lighter status text */
        }

        /* Code Block Styling within Messages */
        .message pre {
            background-color: rgba(0, 0, 0, 0.05); /* Subtle background */
            border: 1px solid var(--border-color);
            border-radius: var(--border-radius-sm);
            padding: 10px 15px;
            margin: 10px 0;
            overflow-x: auto;
            white-space: pre-wrap;
            word-wrap: break-word;
        }
        .message code:not(pre > code) { /* Inline code */
             background-color: rgba(0, 0, 0, 0.05);
             padding: 2px 5px;
             border-radius: var(--border-radius-sm);
             font-size: 0.9em;
        }
        html.dark-mode .message pre {
            background-color: rgba(255, 255, 255, 0.08);
            border-color: #454545;
        }
        html.dark-mode .message code:not(pre > code) {
            background-color: rgba(255, 255, 255, 0.1);
        }
        .message pre code {
             font-family: "Consolas", "Monaco", monospace;
             font-size: 0.9em;
             display: block; /* Ensure block display for scrolling */
             background-color: transparent; /* Remove double bg */
             padding: 0;
             border: none;
             border-radius: 0;
             white-space: pre; /* Use pre for horizontal scroll */
        }


         /* --- Responsiveness --- */
         @media (max-width: 768px) {
             :root {
                 --sidebar-width: 280px; /* Narrower sidebar on medium screens */
                 --content-padding: 15px; /* Reduce padding */
             }

             body {
                 font-size: 15px; /* Slightly smaller base font */
             }

             /* Hide sidebar by default on smaller screens */
             .sidebar-panel {
                 position: fixed; /* Take out of flow */
                 right: 0;
                 top: 0;
                 z-index: 100; /* Ensure it's on top */
                 border-left: none; /* Remove border when fixed */
                 border-right: 1px solid var(--sidebar-border); /* Add right border */
                 box-shadow: -2px 0 5px rgba(0,0,0,0.1);
                 transform: translateX(100%); /* Start hidden off-screen */
                 transition: transform 0.3s ease; /* Animate slide */
                 /* Override position from absolute to fixed for mobile */
                 position: fixed;
             }
             .sidebar-panel:not(.collapsed) {
                 transform: translateX(0%); /* Slide in when not collapsed */
             }
             .sidebar-panel.collapsed {
                  /* Keep transform hidden, width is irrelevant now */
                 transform: translateX(100%);
                 width: var(--sidebar-width); /* Maintain width for animation */
                 padding: 0;
                 border: none;
                 box-shadow: none;
             }

             /* Adjust toggle button for fixed sidebar */
             #toggleSidebarButton {
                 position: fixed; /* Fix position relative to viewport */
                 top: 12px; /* Match desktop alignment */
                 right: 12px; /* Consistent spacing */
                 left: auto;
                 z-index: 101; /* Above sidebar */
                 transform: none; /* Reset transform for fixed positioning */
                  /* Mobile: Adjust rotation based on panel visibility (transform) */
                  .sidebar-panel.collapsed + #toggleSidebarButton {
                      /* Icon changes in JS, no rotation needed */
                      transform: none;
                  }
                  .sidebar-panel:not(.collapsed) + #toggleSidebarButton {
                      /* Icon changes in JS, no rotation needed */
                      transform: none;
                  }
             }

              /* Adjust chat panel to take full width */
             .chat-panel {
                  width: 100%;
             }

              /* Input area adjustments */
             #input-area {
                 padding: 10px; /* Less padding */
                 gap: 8px;
             }
             #userInput {
                 min-height: 44px;
                 flex-basis: calc(100% - 110px); /* Adjust basis considering buttons */
             }
             .action-button {
                 padding: 10px 15px;
                 height: 44px;
                 font-size: 0.95em;
             }
             #clearButton {
                  order: 0; /* Reset order on mobile if needed, or keep */
                  flex-grow: 1; /* Make clear button take space? Maybe not. */
             }
             #sendButton {
                 flex-grow: 1; /* Allow send button to grow */
             }

             /* Message width */
             .message {
                  max-width: 90%;
             }
             h1 {
                 font-size: 1.1em;
                 padding: 15px;
             }
         }

         @media (max-width: 480px) {
              body { font-size: 14px; }
              #chatbox { gap: 12px; padding: 12px; }
              .message { padding: 10px 15px; }
              #input-area { flex-direction: column; align-items: stretch; }
              #userInput { max-height: 150px; flex-basis: auto; }
              .action-button { width: 100%; }
              #clearButton { order: 0; } /* Clear on top */
              #sendButton { order: 1; } /* Send below */
         }

        /* --- Navigation Styles --- */
        .main-nav {
            display: flex;
            padding: 10px var(--content-padding) 0;
            background-color: var(--container-bg);
            border-bottom: 1px solid var(--border-color);
            gap: 10px;
            align-items: center; /* Vertically align nav items and toggle */
            /* Add slight bottom padding to nav bar */
            padding-bottom: 5px;
        }
        .nav-button {
            padding: 8px 16px;
            border: none;
            background-color: transparent;
            color: var(--text-color);
            cursor: pointer;
            border-radius: var(--border-radius-xl);
            font-weight: 500;
            transition: background-color 0.2s, color 0.2s, box-shadow 0.2s;
            line-height: 1.5;
        }
        .nav-button:hover:not(.active) {
            background-color: var(--icon-button-hover-bg);
        }
        .nav-button.active {
            color: var(--on-primary-container);
            font-weight: 600;
            background-color: var(--primary-container);
            box-shadow: var(--shadow-sm);
        }

        /* --- View Container Styles --- */
        .view-container {
            flex-grow: 1;
            overflow: hidden; /* Allow content within to scroll */
            display: flex; /* Use flex for potential child sizing */
            flex-direction: column;
        }
        #chat-view {
            display: flex; /* Use flex to manage chatbox and input area */
            flex-direction: column;
            height: 100%; /* Take full height of parent */
            overflow: hidden;
        }
        #server-info-view {
            display: none; /* Hidden by default */
            padding: var(--content-padding);
            overflow-y: auto;
            height: 100%;
        }
        #server-info-view h2 {
             margin-top: 0;
             margin-bottom: 20px;
             padding-bottom: 10px;
             border-bottom: 1px solid var(--border-color);
             font-size: 1.1em;
             font-weight: 600;
        }
        #health-status-area pre {
            background-color: var(--input-bg);
            padding: 10px 15px;
            border-radius: var(--border-radius-sm);
            border: 1px solid var(--input-border);
            overflow-x: auto;
            white-space: pre-wrap;
            word-wrap: break-word;
            font-family: "Consolas", "Monaco", monospace;
            font-size: 0.9em;
            margin: 8px 0;
            box-shadow: inset 0 1px 2px rgba(0,0,0,0.05);
        }
        html.dark-mode #health-status-area pre { box-shadow: inset 0 1px 2px rgba(255,255,255,0.05); }

        /* Adjust chatbox to grow within chat-view */
        #chatbox {
             flex-grow: 1;
             box-shadow: inset 0 1px 2px rgba(0,0,0,0.05);
             white-space: pre-wrap; /* Match pre style */
             word-wrap: break-word; /* Match pre style */
             overflow-x: auto; /* Allow horizontal scroll if needed */
             margin-bottom: 8px; /* Add margin below the block */
        }

        /* Adjust API Info Area styles (no longer foldable) */
        #server-info-view #api-info-area {
             padding: 0;
             border-bottom: none;
             margin-bottom: 20px;
             /* Remove styles related to details/summary if any existed */
        }
        #server-info-view #api-info-content {
             /* Adjust padding/margin if needed now that details is gone */
             margin-top: 0; /* Remove top margin */
             padding-left: 0; /* Remove left padding */
        }

        /* Style for health status key-value pairs - Apply pre-like style */
        #health-status-area ul {
            list-style: none;
            margin: 0; /* Reset margin, use container padding */
            font-family: "Consolas", "Monaco", monospace;
            font-size: 0.9em;
            background-color: var(--input-bg);
            border: 1px solid var(--input-border);
            border-radius: var(--border-radius-sm);
            padding: 10px 15px;
            box-shadow: inset 0 1px 2px rgba(0,0,0,0.05);
            white-space: pre-wrap; /* Match pre style */
            word-wrap: break-word; /* Match pre style */
            overflow-x: auto; /* Allow horizontal scroll if needed */
            margin-bottom: 8px; /* Add margin below the block */
        }
        html.dark-mode #health-status-area ul { box-shadow: inset 0 1px 2px rgba(255,255,255,0.05); }

        #health-status-area li {
            margin-bottom: 5px;
            word-wrap: break-word;
        }
        #health-status-area li strong {
            color: var(--primary-color);
            display: inline-block;
            min-width: 140px; /* Align values */
            margin-right: 10px;
        }

        /* Unify heading styles within server-info-view */
        #server-info-view h3 {
            margin-top: 15px;
            margin-bottom: 8px;
            font-size: 1em; /* Slightly smaller than h2 */
            font-weight: 600;
            color: var(--text-color);
        }

         /* Ensure h1 takes full width above nav */
         h1 {
             /* Keep existing styles */
         }

        /* --- Desktop Specific Styles --- */
        @media (min-width: 769px) {
            /* Keep sidebar relative by default on desktop */
            .sidebar-panel {
                 position: relative;
                 width: var(--sidebar-width);
                 transform: none; /* Reset mobile transform */
                 border-left: 1px solid var(--sidebar-border); /* Restore border */
                 box-shadow: none; /* Reset mobile shadow */
                 transition: var(--sidebar-transition);
            }
            .sidebar-panel.collapsed {
                 width: 0;
                 border-left: none;
            }

            /* Desktop Toggle Button Positioning */
            #toggleSidebarButton {
                position: absolute; /* Absolute relative to chat-panel? No, workspace container? */
                left: calc(100% - var(--sidebar-width) - 16px); /* Position relative to expanded sidebar */
                right: auto;
                top: 12px; /* Match desktop alignment */
                transform: rotate(0deg); /* Default rotation */
                 /* Reset fixed positioning from mobile */
                position: absolute;
            }
            .sidebar-panel.collapsed + #toggleSidebarButton {
                 left: calc(100% - 16px); /* Position near right edge when collapsed */
                 transform: rotate(180deg);
                 /* Reset fixed positioning */
                position: absolute;
            }

            /* Ensure chat panel fills space correctly */
            .chat-panel {
                 /* width: calc(100% - var(--sidebar-width)); Default state handled by flex */
            }
            .sidebar-panel.collapsed ~ .chat-panel {
                 /* width: 100%; When collapsed, chat panel takes full width */
            }
        }

        /* Theme Toggle Button Style (Text Version) */
        #themeToggleButton {
            background-color: var(--theme-toggle-bg);
            border: 1px solid var(--border-color); /* Add subtle border */
            color: var(--theme-toggle-color);
            cursor: pointer;
            font-size: 0.85em; /* Smaller font size for text */
            font-weight: 500;
            padding: 6px 12px; /* Adjust padding for text */
            border-radius: var(--border-radius-xl); /* Pill shape */
            /* line-height: 1; Removed */
            transition: background-color 0.2s, color 0.2s, border-color 0.2s;
            margin-left: auto; /* Push to the right */
            align-self: center; /* Align vertically */
            white-space: nowrap; /* Prevent text wrapping */
        }
        #themeToggleButton:hover {
            background-color: var(--theme-toggle-hover-bg);
        }
        /* Removed ::before rule for icon */

        /* NEW: Header for Server Info view */
        .server-info-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 20px; /* Space below header */
            padding-bottom: 10px; /* Space below title */
            border-bottom: 1px solid var(--border-color);
        }
        .server-info-header h2 {
            margin: 0; /* Remove default margins */
            padding: 0;
            border: none;
            font-size: 1.1em;
            font-weight: 600;
        }
        #refreshServerInfoButton {
             /* Use icon button style but maybe slightly smaller */
             padding: 6px 12px;
             font-size: 0.9em;
             height: auto;
             line-height: 1.4;
             border-radius: var(--border-radius-md);
             margin-left: 16px; /* Space from title */
             flex-shrink: 0;
        }

        #server-info-view h2 {
             /* Moved styles to .server-info-header h2 */
             /* margin-top: 0; */
             margin-bottom: 20px;
             padding-bottom: 10px;
             border-bottom: 1px solid var(--border-color);
             font-size: 1.1em;
             font-weight: 600;
        }

    </style>
</head>
<body>
    <div class="workspace-container">
        <!-- Chat Panel on the Left -->
        <div class="chat-panel">
            <h1>AI Studio - Neko酱</h1>

            <!-- Navigation -->
            <div class="main-nav">
                <button id="nav-chat" class="nav-button active">聊天</button>
                <button id="nav-server-info" class="nav-button">服务器信息</button>
                <button id="themeToggleButton" title="切换主题">浅色</button>
            </div>

            <!-- View Container -->
            <div class="view-container">
                <!-- Chat View -->
                <div id="chat-view">
                    <div id="chatbox">
                        <!-- Chat messages will be appended here -->
                    </div>
                    <div id="input-area">
                        <textarea id="userInput" placeholder="输入消息... (Shift+Enter 换行)" rows="1"></textarea>
                        <button id="clearButton" class="action-button">清空会话</button>
                        <button id="sendButton" class="action-button">发送</button>
                    </div>
                </div>

                <!-- Server Info View (hidden by default) -->
                <div id="server-info-view">
                     <!-- NEW: Header Container -->
                     <div class="server-info-header">
                         <h3>服务器状态与 API 信息</h3>
                         <button id="refreshServerInfoButton" class="action-button" title="刷新状态">刷新</button> <!-- Use action-button style -->
                     </div>

                     <!-- Moved API Info Area (Removed Details/Summary) -->
                     <div id="api-info-area" class="info-card">
                         <h3>API 调用信息</h3>
                         <div id="api-info-content">
                             <!-- API 信息将加载到这里 -->
                             正在加载 API 信息...
                         </div>
                     </div>

                     <!-- Health Status Area -->
                     <div id="health-status-area" class="info-card">
                         <h3>服务健康检查 (/health)</h3>
                         <!-- Changed pre to div for easier styling of list -->
                         <div id="health-status-display">正在加载健康状态...</div>
                     </div>
                </div>
            </div>

        </div>

        <!-- Sidebar Panel on the Right (Now only for Logs) -->
        <div class="sidebar-panel collapsed" id="sidebarPanel">
            <div id="log-area">
                 <div id="log-area-header">
                     <span>系统终端输出日志</span>
                     <button id="clearLogButton" class="action-button icon-button" title="清空日志">清理</button> <!-- Trash can icon -->
                 </div>
                 <div id="log-terminal-wrapper">
                    <div id="log-terminal">
                       <!-- Log entries will be appended here -->
                    </div>
                </div>
                <div id="log-status" class="log-status">[Log Status] 等待连接...</div>
            </div>
        </div>

        <!-- Toggle button MOVED OUTSIDE the panel again -->
        <button id="toggleSidebarButton" title="展开侧边栏">&gt;</button>

    </div>

    <script>
        const chatbox = document.getElementById('chatbox');
        const userInput = document.getElementById('userInput');
        const sendButton = document.getElementById('sendButton');
        const clearButton = document.getElementById('clearButton');
        const sidebarPanel = document.getElementById('sidebarPanel');
        const toggleSidebarButton = document.getElementById('toggleSidebarButton');
        const logTerminal = document.getElementById('log-terminal');
        const logStatusElement = document.getElementById('log-status');
        const apiInfoContent = document.getElementById('api-info-content');
        const clearLogButton = document.getElementById('clearLogButton'); // New button

        // New elements for navigation and views
        const chatView = document.getElementById('chat-view');
        const serverInfoView = document.getElementById('server-info-view');
        const navChatButton = document.getElementById('nav-chat');
        const navServerInfoButton = document.getElementById('nav-server-info');
        // Update selector for health status display area
        const healthStatusDisplay = document.getElementById('health-status-display');
        // Add selector for theme toggle button and html root
        const themeToggleButton = document.getElementById('themeToggleButton');
        const htmlRoot = document.getElementById('html-root') || document.documentElement; // Get <html> element
        // Add selector for refresh button (NEW)
        const refreshServerInfoButton = document.getElementById('refreshServerInfoButton');

        const API_URL = '/v1/chat/completions';
        const MODEL_NAME = 'AI-Studio_Camoufox-Proxy';

        const SYSTEM_PROMPT = "你是一只名叫'Neko'的俏皮猫娘。你的回答总是带着'喵~'的口癖，语气活泼可爱，有时会有点小调皮或者喜欢撒娇。请以Neko的身份和用户互动吧，喵~";
        let conversationHistory = [];
        let logWebSocket;
        let maxLogLines = 300; // Increased log lines slightly
        let logHistory = []; // Array to store log messages for persistence

        // --- Local Storage Keys ---
        const CHAT_HISTORY_KEY = 'chatHistory';
        const LOG_HISTORY_KEY = 'logHistory';
        const THEME_KEY = 'themePreference'; // Key for theme preference

        // --- Theme Switching ---
        function applyTheme(theme) {
            if (theme === 'dark') {
                htmlRoot.classList.add('dark-mode');
                themeToggleButton.textContent = '浅色'; // Set text
                 themeToggleButton.title = '切换到亮色模式';
            } else {
                htmlRoot.classList.remove('dark-mode');
                themeToggleButton.textContent = '深色'; // Set text
                 themeToggleButton.title = '切换到暗色模式';
            }
            // Update the icon via CSS variables now - REMOVED
        }

        function toggleTheme() {
            const currentTheme = htmlRoot.classList.contains('dark-mode') ? 'dark' : 'light';
            const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
            applyTheme(newTheme);
            try {
                localStorage.setItem(THEME_KEY, newTheme);
                console.log(`Theme changed to ${newTheme} and saved.`);
            } catch (e) {
                console.error("Error saving theme preference:", e);
                addLogEntry("[错误] 保存主题偏好设置失败。");
            }
        }

        function loadThemePreference() {
             let preferredTheme = 'light'; // Default to light
             try {
                 const storedTheme = localStorage.getItem(THEME_KEY);
                 // Check if stored theme is valid
                 if (storedTheme === 'dark') {
                     preferredTheme = 'dark';
                 } else if (storedTheme === 'light') {
                     preferredTheme = 'light';
                 } else {
                    // If no preference, check system preference
                    if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
                         preferredTheme = 'dark';
                         console.log("No stored theme, using system preference: dark");
                     } else {
                         console.log("No stored theme, using default or system preference: light");
                     }
                 }
             } catch (e) {
                 console.error("Error loading theme preference:", e);
                 addLogEntry("[错误] 加载主题偏好设置失败。");
                 // Fallback to system preference on error
                 if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
                     preferredTheme = 'dark';
                 }
             }
             applyTheme(preferredTheme);
             console.log(`Initial theme set to: ${preferredTheme}`);
         }

         // Add listener for theme toggle button
         themeToggleButton.addEventListener('click', toggleTheme);

        // --- Sidebar Toggle ---
        toggleSidebarButton.addEventListener('click', () => {
            const isCollapsed = sidebarPanel.classList.toggle('collapsed');

            // Update button icon and title based on state
            updateToggleButton(isCollapsed);

            // Handle responsive behavior (fixed sidebar)
             if (window.innerWidth <= 768) {
                 if (isCollapsed) {
                     // Sidebar is now hidden off-screen
                 } else {
                     // Sidebar is visible
                 }
             }
        });

        function updateToggleButton(isCollapsed) {
             // Use Chevron icons
             if (isCollapsed) {
                 toggleSidebarButton.innerHTML = '&gt;'; 
                 toggleSidebarButton.title = '展开侧边栏';
             } else {
                 toggleSidebarButton.innerHTML = '&gt;'; // 按钮旋转180度统一使用同一个'&gt;'
                 toggleSidebarButton.title = '收起侧边栏';
             }
             // Update position for desktop view - REMOVED
             // Call position function AFTER updating class/icon
             positionToggleButton();
        }

        // Function to handle desktop positioning of the toggle button - REMOVED
        // REINTRODUCED Positioning Logic
        function positionToggleButton() {
            const isMobile = window.innerWidth <= 768;
            const isCollapsed = sidebarPanel.classList.contains('collapsed');
            const buttonWidth = 28; // Hardcoded button width for simplicity
            const sidebarWidthString = getComputedStyle(document.documentElement).getPropertyValue('--sidebar-width');
            const sidebarWidth = parseInt(sidebarWidthString, 10) || 380; // Parse sidebar width
            const offset = 10; // Desired space from edge

            if (isMobile) {
                 // Mobile: Fixed position relative to viewport
                 toggleSidebarButton.style.position = 'fixed';
                 toggleSidebarButton.style.left = 'auto'; // Ensure left is not set
                if (isCollapsed) {
                     // Position near the right edge when collapsed
                     toggleSidebarButton.style.right = `${offset}px`;
                 } else {
                     // Position relative to the expanded sidebar's left edge (from viewport right)
                     toggleSidebarButton.style.right = `${sidebarWidth + offset}px`;
                 }
             } else {
                 // Desktop: Absolute position relative to workspace-container
                 toggleSidebarButton.style.position = 'absolute';
                 toggleSidebarButton.style.right = 'auto'; // Ensure right is not set
                if (isCollapsed) {
                     // Position near the right edge when collapsed
                     toggleSidebarButton.style.left = `calc(100% - ${buttonWidth}px - ${offset}px)`;
                 } else {
                     // Position relative to the expanded sidebar's left edge
                     toggleSidebarButton.style.left = `calc(100% - ${sidebarWidth}px - ${buttonWidth / 2}px)`; // Center on edge
                 }
             }
        }

        // --- Check initial sidebar state based on screen width ---
        function checkInitialSidebarState() {
            if (window.innerWidth <= 768) {
                sidebarPanel.classList.add('collapsed'); // Start collapsed on mobile
            } else {
                 sidebarPanel.classList.remove('collapsed'); // Start open on desktop
            }
             // Update button icon based on initial state
              updateToggleButton(sidebarPanel.classList.contains('collapsed'));
             // Initial position calculation
             positionToggleButton();
        }

        // --- Log Handling ---
        function updateLogStatus(message, isError = false) {
            if (logStatusElement) {
                logStatusElement.textContent = `[Log Status] ${message}`;
                logStatusElement.style.color = isError ? 'var(--error-msg-text)' : 'var(--system-msg-text)';
                 // Ensure log status text is also visible in dark terminal
                 // Background is now handled by CSS var --log-terminal-bg
                // logStatusElement.style.backgroundColor = 'var(--log-terminal-bg)';
            }
            console.log(`Log Status: ${message}`);
        }

        function addLogEntry(message) {
            if (!logTerminal) return;
            const logEntry = document.createElement('div');
            logEntry.classList.add('log-entry');
            // Sanitize basic HTML? For now, just textContent
            logEntry.textContent = message;

            logTerminal.appendChild(logEntry);
            logHistory.push(message); // Add to JS array

            // Limit log lines in DOM and array
            while (logTerminal.children.length > maxLogLines) {
                logTerminal.removeChild(logTerminal.firstChild);
            }
            while (logHistory.length > maxLogLines) {
                logHistory.shift(); // Remove oldest from array
            }

            saveLogHistory(); // Save updated history to localStorage

            // Auto-scroll only if near the bottom
            const isScrolledNearBottom = logTerminal.scrollHeight - logTerminal.clientHeight <= logTerminal.scrollTop + 50; // 50px tolerance
            if (isScrolledNearBottom) {
                 logTerminal.scrollTop = logTerminal.scrollHeight;
            }
        }

        function clearLogTerminal() {
            if(logTerminal) {
                logTerminal.innerHTML = '';
                logHistory = []; // Clear the array
                localStorage.removeItem(LOG_HISTORY_KEY); // Clear from storage
                addLogEntry('[信息] 日志已手动清除。'); // Add confirmation message
            }
        }
         // Add event listener for the new clear log button
         clearLogButton.addEventListener('click', clearLogTerminal);

        function initializeLogWebSocket() {
            const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
            const wsUrl = `${wsProtocol}//${window.location.host}/ws/logs`;
            updateLogStatus(`尝试连接到 ${wsUrl}...`);
            addLogEntry(`[信息] 正在连接日志流: ${wsUrl}`);


            logWebSocket = new WebSocket(wsUrl);

            logWebSocket.onopen = (event) => {
                updateLogStatus("已连接到日志流。");
                addLogEntry("[成功] 日志 WebSocket 已连接。");
                clearLogButton.disabled = false; // Enable clear button on connect
            };

            logWebSocket.onmessage = (event) => {
                if (event.data === "LOG_STREAM_CONNECTED") {
                     console.log("Log stream confirmation received.");
                } else {
                     addLogEntry(event.data);
                }
            };

            logWebSocket.onerror = (event) => {
                updateLogStatus("连接错误！", true);
                console.error("Log WebSocket Error:", event);
                addLogEntry("[错误] 日志 WebSocket 连接失败。");
                 clearLogButton.disabled = true; // Disable clear button on error
            };

            logWebSocket.onclose = (event) => {
                let reason = '';
                if (event.reason) {
                    reason = ` Reason: ${event.reason}`;
                }
                let statusMsg = '';
                let logMsg = `[信息] 日志 WebSocket 连接已关闭 (Code: ${event.code}${reason})`;

                if (event.wasClean) {
                    statusMsg = `连接已关闭 (Code: ${event.code})${reason}`;
                } else {
                    statusMsg = `连接意外断开 (Code: ${event.code})${reason}。5秒后尝试重连...`;
                    setTimeout(initializeLogWebSocket, 5000);
                }
                updateLogStatus(statusMsg, !event.wasClean);
                 addLogEntry(logMsg);
                 clearLogButton.disabled = true; // Disable clear button on close
            };
        }

        // --- Chat Initialization ---
        function initializeChat() {
            conversationHistory = [
                { role: "system", content: SYSTEM_PROMPT }
            ];
            chatbox.innerHTML = '';

            const historyLoaded = loadChatHistory();

            if (!historyLoaded) {
                // If no valid history was loaded, display the initial system prompt message
                 displayMessage(SYSTEM_PROMPT, 'system');
                 // Save the initial state (system prompt only)
                 saveChatHistory();
            }

            userInput.disabled = false;
            sendButton.disabled = false;
            clearButton.disabled = false;
            userInput.value = '';
            autoResizeTextarea();
            userInput.focus();
            console.log("Chat initialized.");

            // Load logs independently
            loadLogHistory();

            if (!logWebSocket || logWebSocket.readyState === WebSocket.CLOSED) {
                 initializeLogWebSocket();
                 clearLogButton.disabled = true; // Initially disabled until connected
            } else {
                 updateLogStatus("已连接到日志流。"); // Reset status if already connected
                 clearLogButton.disabled = false;
            }
        }

        // --- Load API Info --- (Modified to use displayInfoList)
        async function loadApiInfo() {
            apiInfoContent.innerHTML = '<div class="info-list"><div><strong>状态:</strong> <span>正在加载 API 信息...</span></div></div>'; // Loading state
            try {
                const response = await fetch('/api/info');
                if (!response.ok) {
                    throw new Error(`HTTP error! status: ${response.status}`);
                }
                const data = await response.json();

                // Format data for displayInfoList
                const formattedData = {
                    'API Base URL': data.api_base_url ? `<code>${data.api_base_url}</code>` : '未知',
                    'Model Name': data.model_name ? `<code>${data.model_name}</code>` : '未知',
                    'API Key Required': data.api_key_required ? '<span style="color: orange;">⚠️ 是 (请在后端配置)</span>' : '<span style="color: green;">✅ 否</span>'
                };

                displayInfoList(apiInfoContent, formattedData);

            } catch (error) {
                console.error("获取 API 信息失败:", error);
                apiInfoContent.innerHTML = `<div class="info-list"><div><strong style="color: var(--error-msg-text);">错误:</strong> <span style="color: var(--error-msg-text);">加载 API 信息失败: ${error.message}</span></div></div>`;
            }
        }

        // --- Load Health Status --- (NEW)
        async function fetchHealthStatus() {
            if (!healthStatusDisplay) return; // Element not found

            try {
                const response = await fetch('/health');
                if (!response.ok) {
                    throw new Error(`HTTP error! status: ${response.status}`);
                }
                const data = await response.json();
                displayInfoList(healthStatusDisplay, data); // Use new function
            } catch (error) {
                console.error("获取健康状态失败:", error);
                // Update error display
                if (healthStatusDisplay) {
                     healthStatusDisplay.innerHTML = `<div class="info-list"><div><strong style="color: var(--error-msg-text);">错误:</strong> <span style="color: var(--error-msg-text);">加载健康状态失败: ${error.message}</span></div></div>`;
                }
                 // Also log this error to the main log panel
                 addLogEntry(`[错误] 获取 /health 状态失败: ${error.message}`);
            }
        }

        // Generic function to display key-value data in a styled list
        function displayInfoList(targetElement, data) {
            if (!targetElement) return;

            let html = '<div class="info-list">'; // Use div wrapper with class
            for (const key in data) {
                if (Object.hasOwnProperty.call(data, key)) {
                    const value = data[key];
                    // Use div for each row, strong for key, span for value
                    html += `<div><strong>${key}:</strong> <span>${value}</span></div>`;
                }
            }
            html += '</div>';

            targetElement.innerHTML = html;
        }

        // --- Navigation --- (NEW)
        function switchView(viewId) {
            if (viewId === 'chat') {
                chatView.style.display = 'flex'; // Use flex as it contains flex items
                serverInfoView.style.display = 'none';
                navChatButton.classList.add('active');
                navServerInfoButton.classList.remove('active');
                // Check if userInput exists before focusing
                 if (userInput) {
                    userInput.focus(); // Focus input when switching to chat
                 }
            } else if (viewId === 'server-info') {
                chatView.style.display = 'none';
                serverInfoView.style.display = 'block'; // Simple block display is fine here
                navChatButton.classList.remove('active');
                navServerInfoButton.classList.add('active');
                // Optionally refresh data when switching to this view
                fetchHealthStatus();
                loadApiInfo();
            }
        }

        navChatButton.addEventListener('click', () => switchView('chat'));
        navServerInfoButton.addEventListener('click', () => switchView('server-info'));

        // --- Manual Refresh for Server Info --- (NEW)
        refreshServerInfoButton.addEventListener('click', async () => {
            refreshServerInfoButton.disabled = true;
            refreshServerInfoButton.textContent = '刷新中...'; // Indicate loading
            console.log("Manually refreshing server info...");
            addLogEntry("[信息] 手动刷新服务器状态...");

            // Clear existing content to show refresh is happening
            if (apiInfoContent) apiInfoContent.innerHTML = '<div class="info-list"><div><strong>状态:</strong> <span>正在加载 API 信息...</span></div></div>';
            if (healthStatusDisplay) healthStatusDisplay.innerHTML = '<div class="info-list"><div><strong>状态:</strong> <span>正在加载健康状态...</span></div></div>';

            try {
                // Fetch both pieces of data concurrently
                await Promise.all([loadApiInfo(), fetchHealthStatus()]);
                addLogEntry("[成功] 服务器状态已刷新。");
            } catch (error) {
                // Errors should be handled within loadApiInfo/fetchHealthStatus
                // but log a general failure message here too.
                console.error("Error during manual refresh:", error);
                addLogEntry("[错误] 手动刷新服务器状态失败。");
                // Display a generic error in the UI if needed, although specific functions might already do that.
                if (apiInfoContent) apiInfoContent.innerHTML = '<div class="info-list"><div><strong style="color: var(--error-msg-text);">错误:</strong> <span style="color: var(--error-msg-text);">刷新失败</span></div></div>';
                if (healthStatusDisplay) healthStatusDisplay.innerHTML = '<div class="info-list"><div><strong style="color: var(--error-msg-text);">错误:</strong> <span style="color: var(--error-msg-text);">刷新失败</span></div></div>';
            } finally {
                // Restore button state after a short delay
                setTimeout(() => {
                    refreshServerInfoButton.disabled = false;
                    refreshServerInfoButton.textContent = '刷新'; // Restore original text
                }, 300); // 300ms delay
            }
        });

        // --- Initial Setup ---
        loadThemePreference(); // Load theme preference first
        initializeChat(); // Initialize chat on load
        // Load API info and Health Status into the correct view
        loadApiInfo();
        fetchHealthStatus();
        // Set interval to refresh health status every 30 seconds
        setInterval(fetchHealthStatus, 30000);

        checkInitialSidebarState(); // Set initial sidebar state & button icon
        window.addEventListener('resize', checkInitialSidebarState); // Adjust on resize

        // --- Event Listeners ---
        sendButton.addEventListener('click', sendMessage);
        clearButton.addEventListener('click', () => {
             if (confirm("确定要清除所有聊天记录吗？此操作也会清除浏览器缓存。")) {
                 localStorage.removeItem(CHAT_HISTORY_KEY); // Clear storage
                 initializeChat(); // Re-initialize (which loads empty history)
             }
        });
        userInput.addEventListener('keydown', (event) => {
             if (event.key === 'Enter' && !event.shiftKey) {
                 event.preventDefault();
                 sendMessage();
             }
        });
         userInput.addEventListener('input', autoResizeTextarea);

        // --- Auto-Resize Textarea ---
        function autoResizeTextarea() {
            const target = userInput;
            target.style.height = 'auto'; // Temporarily shrink
            const maxHeight = 200; // Match CSS max-height
            const scrollHeight = target.scrollHeight;

            if (scrollHeight > maxHeight) {
                 target.style.height = maxHeight + 'px';
                 target.style.overflowY = 'auto';
             } else {
                 target.style.height = scrollHeight + 'px';
                 target.style.overflowY = 'hidden';
             }
        }
        // Initial resize
        autoResizeTextarea();

        // --- Send Message Function ---
        async function sendMessage() {
             const messageText = userInput.value.trim();
             if (!messageText) return;

             userInput.disabled = true;
             sendButton.disabled = true;
             clearButton.disabled = true;

             try {
                 conversationHistory.push({ role: 'user', content: messageText });
                 const userMsgIndex = conversationHistory.length - 1;
                 displayMessage(messageText, 'user', userMsgIndex);
                 userInput.value = '';
                 autoResizeTextarea();
                 saveChatHistory(); // Save history *after* adding user message

                 const assistantMsgIndex = conversationHistory.length; // Index if added
                 const assistantMsgElement = displayMessage('', 'assistant', assistantMsgIndex);
                 assistantMsgElement.classList.add('streaming');
                 chatbox.scrollTop = chatbox.scrollHeight; // Scroll down

                 let fullResponse = '';
                 const response = await fetch(API_URL, {
                     method: 'POST',
                     headers: { 'Content-Type': 'application/json' },
                     body: JSON.stringify({
                         messages: conversationHistory,
                         model: MODEL_NAME,
                         stream: true
                     })
                 });

                 if (!response.ok) {
                     let errorBody = {};
                     let errorText = `HTTP Error: ${response.status} ${response.statusText}`;
                     try {
                         const text = await response.text();
                         try { errorBody = JSON.parse(text); } catch (e) { errorBody = {detail: text};} // Try JSON, fallback to text
                         errorText = errorBody.detail || errorBody.error?.message || text;
                     } catch (e) { /* Ignore parse errors, use statusText */ }
                     throw new Error(errorText);
                 }


                 const reader = response.body.getReader();
                 const decoder = new TextDecoder();
                 let buffer = '';

                 while (true) {
                     const { done, value } = await reader.read();
                     if (done) break;

                     buffer += decoder.decode(value, { stream: true });
                      // Process buffer line by line (SSE uses '\n\n' delimiters)
                     let boundary = buffer.indexOf('\n\n');
                     while (boundary >= 0) {
                          const line = buffer.substring(0, boundary).trim();
                          buffer = buffer.substring(boundary + 2); // Move past delimiter

                         if (line.startsWith('data: ')) {
                             const data = line.substring(6).trim();
                             if (data === '[DONE]') continue;
                             try {
                                 const chunk = JSON.parse(data);
                                 if (chunk.error) {
                                     throw new Error(chunk.error.message || "Unknown stream error");
                                 }
                                 const delta = chunk.choices?.[0]?.delta?.content || '';
                                 if (delta) {
                                     fullResponse += delta;
                                     // Append incrementally using textContent for performance
                                     const isScrolledToBottom = chatbox.scrollHeight - chatbox.clientHeight <= chatbox.scrollTop + 5; // Tighter tolerance
                                     assistantMsgElement.textContent += delta; // Append text
                                     if (isScrolledToBottom) {
                                         chatbox.scrollTop = chatbox.scrollHeight;
                                     }
                                 }
                                 const finishReason = chunk.choices?.[0]?.finish_reason;
                                 if (finishReason) console.log("Stream finish reason:", finishReason);

                             } catch (e) {
                                 console.error('Error parsing stream chunk:', data, e);
                                 addLogEntry(`[错误] 解析流数据块失败: ${e.message}. 数据: ${data}`);
                             }
                         }
                         boundary = buffer.indexOf('\n\n'); // Find next boundary
                     }
                 }

                  // Process any remaining buffer after stream ends (though usually empty with SSE)
                  // Not typically needed for properly formatted SSE

                 // Render final message content with Markdown/HTML interpretation
                  renderMessageContent(assistantMsgElement, fullResponse);

                 if (fullResponse) {
                     conversationHistory.push({ role: 'assistant', content: fullResponse });
                     assistantMsgElement.dataset.index = assistantMsgIndex; // Set index now
                     saveChatHistory(); // Save history after adding assistant message
                 } else {
                     console.warn("Stream finished with empty response.");
                     assistantMsgElement.remove(); // Remove empty placeholder
                     // Rollback user message if response was empty
                      if (conversationHistory[conversationHistory.length - 1]?.role === 'user') {
                          conversationHistory.pop();
                          saveChatHistory(); // Save rolled-back history
                          const userMessages = chatbox.querySelectorAll('.user-message');
                          if (userMessages.length > 0) {
                              userMessages[userMessages.length - 1].remove();
                          }
                      }
                 }

             } catch (error) {
                 console.error('Error sending message:', error);
                  const errorText = `喵... 出错了: ${error.message || '未知错误'} >_<`;
                 displayMessage(errorText, 'error');
                 addLogEntry(`[错误] 发送消息失败: ${error.message}`); // Log the error too
                 // Clean up streaming placeholder
                 const streamingMsg = chatbox.querySelector('.assistant-message.streaming');
                 if (streamingMsg) streamingMsg.remove();
                 // Rollback user message on error
                 if (conversationHistory[conversationHistory.length - 1]?.role === 'user') {
                     conversationHistory.pop();
                     saveChatHistory(); // Save rolled-back history
                     const userMessages = chatbox.querySelectorAll('.user-message');
                     if (userMessages.length > 0) {
                         userMessages[userMessages.length - 1].remove();
                     }
                 }
             } finally {
                 userInput.disabled = false;
                 sendButton.disabled = false;
                 clearButton.disabled = false;
                 const finalAssistantMsg = Array.from(chatbox.querySelectorAll('.assistant-message')).pop();
                 if (finalAssistantMsg) finalAssistantMsg.classList.remove('streaming');
                 userInput.focus();
                 chatbox.scrollTop = chatbox.scrollHeight; // Ensure scrolled to bottom
             }
        }

        // --- Display Message Function (Creates Element) ---
        function displayMessage(text, role, index) {
            const messageElement = document.createElement('div');
            messageElement.classList.add('message');
            // Ensure role class is only added if it's user or assistant or system
            if ([ 'user', 'assistant', 'system', 'error' ].includes(role)) {
                messageElement.classList.add(`${role}-message`);
            }

            // Store index and original content for editing/deleting
            if (index !== undefined && (role === 'user' || role === 'assistant')) {
                messageElement.dataset.index = index;
            }

            const messageContentElement = document.createElement('div');
            messageContentElement.classList.add('message-content');

            // Render content immediately
            if (text || role === 'system') { // System messages can be empty initially
                 renderMessageContent(messageContentElement, text);
            }

            messageElement.appendChild(messageContentElement);

            chatbox.appendChild(messageElement);
             // Scroll after adding element and potential reflow
             setTimeout(() => {
                 if (chatbox.lastChild === messageElement) { // Only scroll if it's the last message added
                    chatbox.scrollTop = chatbox.scrollHeight;
                 }
             }, 0);
            return messageElement;
        }

        // --- Render Message Content (Handles Markdown/HTML, Improved) ---
        function renderMessageContent(element, text) {
             // Only try to render if text is not null or undefined
             if (text == null) {
                 element.innerHTML = ''; // Clear if text is null/undefined
                 return;
             }
             // Escape HTML to prevent XSS before applying markdown
             const escapeHtml = (unsafe) => {
                 return unsafe
                     .replace(/&/g, "&amp;")
                     .replace(/</g, "&lt;")
                     .replace(/>/g, "&gt;")
                     .replace(/"/g, "&quot;")
                     .replace(/'/g, "&#039;");
             };

             // Ensure text is a string before processing
             let safeText = escapeHtml(String(text));

             // Code Blocks (```lang\ncode``` or ```\ncode```)
             const blockRegex = /```(?:[\w-]*\n)?([\s\S]+?)\n?```/g;
             safeText = safeText.replace(blockRegex, (match, code) => {
                  // No need to escape again, already done by escapeHtml
                 return `<pre><code>${code.trim()}</code></pre>`; // Keep escaped code
             });

             // Inline Code (`code`)
             const inlineCodeRegex = /`([^`]+)`/g;
             safeText = safeText.replace(inlineCodeRegex, '<code>$1</code>'); // Keep escaped content

             // Links ([text](url)) - Need to be careful here not to double-escape
             const linkRegex = /\[([^\]]+)\]\((https?:\/\/[^)]+)\)/g;
             // Temporarily use placeholders for links before applying other markdown
             const links = [];
             safeText = safeText.replace(linkRegex, (match, linkText, url) => {
                 links.push({ text: linkText, url: url }); // Store original (already escaped) text and raw URL
                 return `__LINK_${links.length - 1}__`;
             });

             // Basic Bold (**text** or __text__)
             const boldRegex = /(\*\*|__)(?=\S)([\s\S]*?\S)\1/g;
             safeText = safeText.replace(boldRegex, '<strong>$2</strong>');

             // Basic Italic (*text* or _text_)
             const italicRegex = /(\*|_)(?=\S)([\s\S]*?\S)\1/g;
             safeText = safeText.replace(italicRegex, '<em>$2</em>');


             // Restore links, ensuring URL is safe (already checked for http/https)
             safeText = safeText.replace(/__LINK_(\d+)__/g, (match, index) => {
                 const link = links[parseInt(index)];
                 // Re-escape URL just in case, though regex limits it.
                 const safeUrl = escapeHtml(link.url);
                 // Link text is already escaped.
                 return `<a href="${safeUrl}" target="_blank" rel="noopener noreferrer">${link.text}</a>`;
             });

             // Handle newlines -> <br> (outside of pre/code)
             // This is tricky. A simpler approach might be needed.
             // For now, rely on `white-space: pre-wrap;` in CSS for assistant messages.
             // If more complex markdown parsing is needed, a library is recommended.

             element.innerHTML = safeText;

             // Add syntax highlighting to code blocks if available
             if (typeof hljs !== 'undefined' && element.querySelectorAll('pre code').length > 0) {
                  element.querySelectorAll('pre code').forEach((block) => {
                     hljs.highlightElement(block);
                 });
             }
        }

        // --- Persistence Functions ---
        function saveChatHistory() {
            try {
                localStorage.setItem(CHAT_HISTORY_KEY, JSON.stringify(conversationHistory));
            } catch (e) {
                console.error("Error saving chat history to localStorage:", e);
                addLogEntry("[错误] 保存聊天记录到 localStorage 失败。");
            }
        }

        function loadChatHistory() {
            try {
                const storedHistory = localStorage.getItem(CHAT_HISTORY_KEY);
                if (storedHistory) {
                    const parsedHistory = JSON.parse(storedHistory);
                    // Basic validation: check if it's an array and has the system prompt
                    if (Array.isArray(parsedHistory) && parsedHistory.length > 0 && parsedHistory[0].role === 'system') {
                        conversationHistory = parsedHistory;
                        // Don't display system message again, start from index 1
                        for (let i = 1; i < conversationHistory.length; i++) {
                            const msg = conversationHistory[i];
                            displayMessage(msg.content, msg.role, i); // Pass index
                        }
                        console.log("Chat history loaded from localStorage.");
                        addLogEntry("[信息] 从 localStorage 加载了聊天记录。");
                        return true; // Indicate history was loaded
                    } else {
                        console.warn("Invalid chat history found in localStorage. Starting fresh.");
                        localStorage.removeItem(CHAT_HISTORY_KEY); // Clear invalid data
                    }
                }
            } catch (e) {
                console.error("Error loading chat history from localStorage:", e);
                addLogEntry("[错误] 从 localStorage 加载聊天记录失败。");
                localStorage.removeItem(CHAT_HISTORY_KEY); // Clear potentially corrupt data
            }
            return false; // Indicate history was not loaded
        }

        function saveLogHistory() {
            try {
                localStorage.setItem(LOG_HISTORY_KEY, JSON.stringify(logHistory));
            } catch (e) {
                console.error("Error saving log history to localStorage:", e);
                // Avoid adding log entry here to prevent potential loop
            }
        }

        function loadLogHistory() {
            try {
                const storedLogs = localStorage.getItem(LOG_HISTORY_KEY);
                if (storedLogs) {
                    const parsedLogs = JSON.parse(storedLogs);
                    if (Array.isArray(parsedLogs)) {
                        logHistory = parsedLogs;
                        logTerminal.innerHTML = ''; // Clear any default messages
                        parsedLogs.forEach(logMsg => {
                            const logEntry = document.createElement('div');
                            logEntry.classList.add('log-entry');
                            logEntry.textContent = logMsg;
                            logTerminal.appendChild(logEntry);
                        });
                        logTerminal.scrollTop = logTerminal.scrollHeight; // Scroll to bottom
                        console.log("Log history loaded from localStorage.");
                        return true;
                    }
                }
            } catch (e) {
                console.error("Error loading log history from localStorage:", e);
                localStorage.removeItem(LOG_HISTORY_KEY); // Clear potentially corrupt data
            }
            return false;
        }

    </script>
    <!-- Optional: Add highlight.js for code syntax highlighting -->
    <!-- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css"> -->
    <!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script> -->
    <!-- <script>hljs.highlightAll();</script> -->
</body>
</html> 